package midend;
import midend.IR_Instruction.Branch_Instruction;
import midend.IR_Instruction.Instruction;
import midend.IR_Module;

import midend.IR_Value.IR_BaseBlock;
import midend.IR_Value.IR_FuncBlock;

import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedList;

public class CFG_Builder {
    private static IR_Module module;
    private static HashMap<IR_BaseBlock, ArrayList<IR_BaseBlock>> preMap;// 流图上一个
    private static HashMap<IR_BaseBlock, ArrayList<IR_BaseBlock>> nextMap; //流图下一个

    public CFG_Builder(IR_Module module) {
        this.module = module;
    }

    public static void run() {
        for (IR_FuncBlock function : module.getFuncBlocks()) {
            init_Func(function);
            get_CFG(function);
            // removeUseLessBlock(function);
        }
    }

    public static void init_Func(IR_FuncBlock function) {
        preMap = new HashMap<>();
        nextMap = new HashMap<>();
//        childMap = new HashMap<>();
//        parentMap = new HashMap<>();
//        DFMap = new HashMap<>();
//        for (BasicBlock block : function.getBlockList()) {
//            preMap.put(block, new ArrayList<>());
//            nextMap.put(block, new ArrayList<>());
//            childMap.put(block, new ArrayList<>());
//            DFMap.put(block, new ArrayList<>());
//            parentMap.put(block, null);
//        }
    }

    public static void get_CFG(IR_FuncBlock function) {
        LinkedList<IR_BaseBlock> basicBlocks = function.get_bb_List();
        for (IR_BaseBlock block : basicBlocks) {
            if (!preMap.containsKey(block)) {
                preMap.put(block, new ArrayList<>());
            }
            if (!nextMap.containsKey(block)) {
                nextMap.put(block, new ArrayList<>());
            }
            LinkedList<Instruction> instructions = block.get_Instruction_List();
            Instruction lastInstr = instructions.getLast();
            if (lastInstr instanceof Branch_Instruction && !((Branch_Instruction) lastInstr).br_type()) {
                IR_BaseBlock destBlock = ((Branch_Instruction) lastInstr).get_block1();
                addEdge(block, destBlock);
            }
            else if (lastInstr instanceof Branch_Instruction && ((Branch_Instruction) lastInstr).br_type()) {
                IR_BaseBlock ifTrueBlock = ((Branch_Instruction) lastInstr).get_block1();
                IR_BaseBlock ifFalseBlock = ((Branch_Instruction) lastInstr).get_block2();
                addEdge(block, ifTrueBlock);
                addEdge(block, ifFalseBlock);
            }
        }

        for (IR_BaseBlock block : basicBlocks) {
            block.setPreBlock(preMap.get(block));
            block.setNextBlock(nextMap.get(block));
        }
        //function.setPreMap(preMap);
        //function.setNextMap(nextMap);
    }

    private static void addEdge(IR_BaseBlock fromBlock, IR_BaseBlock toBlock) {
        if (!nextMap.containsKey(fromBlock)) {
            nextMap.put(fromBlock, new ArrayList<>());
        }
        nextMap.get(fromBlock).add(toBlock);
        if (!preMap.containsKey(toBlock)) {
            preMap.put(toBlock, new ArrayList<>());
        }
        preMap.get(toBlock).add(fromBlock);
    }

}
